﻿import { DataSync } from './dataSyncFliter';
import { FBSDataContextBase, DataContextFactory } from './../../domain/fbsDataContext';
import { DataSyncAddEventHandler, DataSyncEventListener, DataSyncUpdateEventHandler, DataSyncDeleteEventHandler } from "./dataSyncEventListener";
import { Server } from '../../server';
import { Guid, Logger } from "../utils";
require("../../domain/old_ldc/server");
import NodeCache = require("node-cache");
import { EntityCopier } from "tiny-entity";
const myCache = new NodeCache();

export class DataSyncWatcher {
    static Current: DataSyncWatcher = new DataSyncWatcher();
    private timer;
    private timerOn: boolean = false;
    private ctx: FBSDataContextBase;
    private syncQueue: NodeCache;
    private createCounter: number = 0;
    private updateCounter: number = 0;
    private deleteCounter: number = 0;
    private logger;

    get totalCounter() {
        return this.createCounter + this.updateCounter + this.deleteCounter;
    }

    constructor() {
        this.ctx = DataContextFactory.GetDataContext();
        this.syncQueue = new NodeCache();
        DataSyncEventListener.Current.RegistEvent(new DataSyncAddEventHandler(this.CreateHandle.bind(this)));
        DataSyncEventListener.Current.RegistEvent(new DataSyncUpdateEventHandler(this.UpdateHandle.bind(this)));
        DataSyncEventListener.Current.RegistEvent(new DataSyncDeleteEventHandler(this.DeleteHandle.bind(this)));
        this.logger = new Logger().getLogger("app");
    }

    private StartTimer(interval: number) {
        if (!this.timerOn) {
            this.timerOn = true;
            this.SetTimer(interval);
        }
    }
    private StopTimer() {
        this.timerOn = false;
        this.ClearTimer();
    }
    private SetTimer(interval: number) {
        this.timer = setInterval(this.DataSyncAction.bind(this), interval);
    }
    private ClearTimer() {
        clearInterval(this.timer);
    }

    private async DataSyncAction() {
        try {
            this.ClearTimer();
            let queueStats = this.syncQueue.getStats();
            console.log("current data queue state：", queueStats);
            if (queueStats.keys > 0) {
                let key = this.syncQueue.keys()[0];
                let item: any = this.syncQueue.get(key);
                item.data.toString = () => {
                    return item.type;
                }
                await this.DomainAction(item.opType, key, item.data);
                this.SetTimer(50);
            }
            else {
                this.StopTimer();
            }
        }
        catch (err) {
            console.error(err);
        }
    }

    private DoCount(type: string) {
        switch (type) {
            case "create": this.createCounter--; break;
            case "update": this.updateCounter--; break;
            case "delete": this.deleteCounter--; break;
        }

        console.log("createCount:", this.createCounter, "updateCount:", this.updateCounter, "deleteCount:", this.deleteCounter);
    }
    private async DomainAction(type, key, data) {
        this.logger.info("数据同步日志，" + type, JSON.stringify(data));
        switch (type) {
            case "create":
                try {
                    await this.ctx.Create(data);
                    this.syncQueue.del(key);
                    this.DoCount("create");
                } catch (error) {
                    console.error(error);
                    //如果同步的错误是有重复的主键，则不需要再次写入
                    if (error.code == -101) {
                        this.syncQueue.del(key);
                        this.DoCount("create");
                    }
                    else if (error.errno == 1062) {
                        this.syncQueue.del(key);
                        this.DoCount("create");
                    }
                    else throw error;
                }
                break;
            case "update":
                try {
                    data = DataSync.Filter.DataSyncFilterManager.Current.Filter(data.toString(), data);
                    await this.ctx.Create(data);
                }
                catch (error) {
                    if (error.errno == 1062 || error.code == -101) {
                        await this.ctx.Update(data);
                        this.CallOnSyncEvent(this, { ActionType: "Update", DataType: data.toString() });
                    }
                    else {
                        this.logger.error("数据同步错误，" + data.toString(), error, JSON.stringify(data));
                        throw "mysql error" + error;
                    }
                }
                finally {
                    this.syncQueue.del(key);
                    this.DoCount("update");
                    console.log("更新成功：", data.toString());
                    break;
                }

            case "delete":
                await this.ctx.Delete(data);
                this.syncQueue.del(key);
                this.DoCount("delete");
                console.log("删除成功：", data.toString());
                break;
        }
    }

    private CreateHandle(sender, args) {
        let obj = (<any>this.ctx).GetEntityObject(args.type);
        if (obj && args.state != 1) {
            let c = obj.clone(args.data, obj, false);
            console.log("Create data：" + args.type, args.id);
            this.syncQueue.set(Guid.GetGuid(), { isOp: false, data: c, opType: "create", type: c.toString() }, 10000);
            this.createCounter++;
            this.StartTimer(200);
        }
    }

    private async UpdateHandle(sender, args) {
        let obj = (<any>this.ctx).GetCtxEntity(args.newItem.type);
        if (obj) {
            console.log("Update data：" + args.newItem.type, args.newItem.id);
            let c = obj.clone(args.newItem.data, obj, false);
            if (c) {
                this.syncQueue.set(Guid.GetGuid(), { isOp: false, data: c, opType: "update", type: c.toString() }, 10000);
                this.updateCounter++;
                this.StartTimer(200);
            }
        }
    }
    private DeleteHandle(sender, args) {
        let obj = (<any>this.ctx).GetEntityObject(args.tableName);
        if (obj) {
            console.log("Delete data：" + args.tableName, args.id);
            obj.id = args.id as string;
            this.syncQueue.set(Guid.GetGuid(), { isOp: false, data: obj, opType: "delete", type: obj.toString() }, 10000);
            this.deleteCounter++;
            this.StartTimer(200);
        }
    }

    private OnSyncEventList: ((sender: any, args: any) => void)[] = [];
    OnSyncEventListener(event: (sender: any, args: any) => void) {
        this.OnSyncEventList.push(event);
    }

    private CallOnSyncEvent(sender: any, args: any) {
        for (let event of this.OnSyncEventList) {
            event(sender, args);
        }
    }
}